function num(a: number, b: number): Array<number> {
  return [a, b]
}
num(1, 2)
function strA(a: string, b: string): Array<string> {
  return [a, b]
}
strA('独孤', '求败')

// sp1 函数泛型
/* 1 泛型优化
语法为函数名字后面跟一个<参数名> 参数名可以随便写 例如我这儿写了T
当我们使用这个函数的时候把参数的类型传进去就可以了 （也就是动态类型） */
function Add<T>(a: T, b: T): Array<T> {
  return [a, b]
}
Add<number>(1, 2)
Add<string>('1', '2')

// 2 也可以使用不同的泛型参数名，只要在数量上和使用方式上能对应上就可以。
function Sub<T, U>(a: T, b: U): Array<T | U> {
  const params: Array<T | U> = [a, b]
  return params
}
Sub<Boolean, number>(false, 1)

// sp1.2 接口泛型
// 定义泛型接口 声明接口的时候 在名字后面加一个<参数>
interface MyInter<T> {
  (arg: T): T
}
function fn<T>(arg: T): T {
  return arg
}
let result: MyInter<number> = fn
result(123)

// sp1.3 对象字面量泛型
let foo: { <T>(arg: T): T }
foo = function <T>(arg: T): T {
  return arg
}
foo(123)

//ps2 泛型约束
/* 期望在一个泛型的变量上面，获取其length参数，
但是，有的数据类型是没有length属性的 
1- 我们就得对使用的泛型进行约束，
我们约束其为具有length属性的类型，这里我们会用到interface,
 */

// function getLegnth<T>(arg:T) {
//   return arg.length //错误没有length
// }
interface Len {
  length: number
}
function getLegnth<T extends Len>(arg: T) {
  return arg.length
}
getLegnth<string>('123')
// getLegnth('123')

/* ps3 使用keyof 约束对象
其中使用了TS泛型和泛型约束。
首先定义了T类型并使用extends关键字继承object类型的子类型，
然后使用keyof操作符获取T类型的所有键，它的返回 类型是联合类型，
最后利用extends关键字约束 K类型必须为keyof T联合类型的子类型 */

/* K extends keyof T K被约束为必须是一个T中的属性键。 extends一个约束 */

function prop<T, K extends keyof T>(obj: T, key: K) {
  return obj[key]
}
let oa = { a: 1, b: 2, c: 3 }
prop(oa, 'a')
// prop(oa, 'd') //此时就会报错发现找不到

// sp4 泛型类
/*声明方法跟函数类似名称后面定义<类型>
使用的时候确定类型new Sub<number>() */
class SubA<T> {
  attr: T[] = []
  add(a: T): T[] {
    return [a]
  }
}

let sq = new SubA<number>()
sq.attr = [1, 2, 3]
sq.add(123)

let strQ = new SubA<string>()
strQ.attr = ['1', '2', '3']
strQ.add('123')
